From 7f66a9828eda0d40a8fdb8fd3e047cc9369be5e9 Mon Sep 17 00:00:00 2001 From: s1lvax Date: Sun, 3 Nov 2024 23:53:05 +0100 Subject: [PATCH 01/13] update to be like main --- src/routes/profile/integrations/+page.svelte | 61 ++++++++++---------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index 4e7761f..1a0f2e1 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -42,37 +42,7 @@ -
- - - - Spotify - - Connect your Spotify account to display your currently playing song - - - - {#if data.spotifyToken} -
-
- -
- -
- {:else} - - {/if} -
-
-
{#if data.chessComUsername} @@ -107,4 +77,35 @@ {/if}
+
+ + + + Spotify (Coming Soon) + + Connect your Spotify account to display your currently playing song + + + + {#if data.spotifyToken} +
+
+ +
+ + +
+ {:else} + + {/if} +
+
+
From b67675000cc0ea55d9f3cfd96274f13bc12f073c Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Mon, 4 Nov 2024 16:40:07 +0400 Subject: [PATCH 02/13] Add LeetCode integration to profile page --- src/routes/profile/integrations/+page.svelte | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index 1a0f2e1..a55208c 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -108,4 +108,34 @@ +
+ + + + LeetCode (Coming Soon) + + Connect your LeetCode account to display your stats + + + + {#if true} +
+
+ +
+
+ {:else} + + {/if} +
+
+ +
From 2d89b82a948ec8428fa7c05a6a602910d8a5de4a Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Mon, 4 Nov 2024 19:18:34 +0400 Subject: [PATCH 03/13] Add LeetCode integration model and form --- prisma/schema.prisma | 11 ++++++ .../components/MyProfile/LeetCodeForm.svelte | 37 +++++++++++++++++++ src/lib/schemas/integration-leetcode.ts | 11 ++++++ .../profile/integrations/+page.server.ts | 6 +++ src/routes/profile/integrations/+page.svelte | 4 +- 5 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/lib/components/MyProfile/LeetCodeForm.svelte create mode 100644 src/lib/schemas/integration-leetcode.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 49cecbd..41fd067 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -29,6 +29,7 @@ model User { spotifyToken SpotifyToken? RecentActivity RecentActivity[] IntegrationChessCom IntegrationChessCom? + IntegrationLeetCode IntegrationLeetCode? CryptoWallets CryptoWallets[] } @@ -108,6 +109,16 @@ model IntegrationChessCom { updatedAt DateTime @updatedAt } +model IntegrationLeetCode { + id Int @id @default(autoincrement()) + usedBy User @relation(fields: [userId], references: [githubId]) + userId Int @unique + username String + visible Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model CryptoWallets { id Int @id @default(autoincrement()) usedBy User @relation(fields: [userId], references: [githubId]) diff --git a/src/lib/components/MyProfile/LeetCodeForm.svelte b/src/lib/components/MyProfile/LeetCodeForm.svelte new file mode 100644 index 0000000..4a9dabf --- /dev/null +++ b/src/lib/components/MyProfile/LeetCodeForm.svelte @@ -0,0 +1,37 @@ + + +
+
+ + + Username + + + + +
+ + Add +
+
+
diff --git a/src/lib/schemas/integration-leetcode.ts b/src/lib/schemas/integration-leetcode.ts new file mode 100644 index 0000000..602ccb3 --- /dev/null +++ b/src/lib/schemas/integration-leetcode.ts @@ -0,0 +1,11 @@ +import { z } from 'zod'; + +export const leetCodeSchema = z.object({ + username: z.string().min(3).max(20) + }); + + +export type LeetcodeSchema = typeof leetCodeSchema; + + + diff --git a/src/routes/profile/integrations/+page.server.ts b/src/routes/profile/integrations/+page.server.ts index 8510854..ba6e388 100644 --- a/src/routes/profile/integrations/+page.server.ts +++ b/src/routes/profile/integrations/+page.server.ts @@ -9,6 +9,7 @@ import { getGitHubUserIdFromImageUrl } from '$lib/utils/getGithubIDFromImage'; import { createRecentActivity } from '$lib/utils/createRecentActivity'; import { unlinkSpotify } from '$lib/utils/spotify/unlinkSpotify'; import { chessComSchema } from '$lib/schemas/integration-chesscom'; +import { leetCodeSchema } from '$lib/schemas/integration-leetcode'; // Define the user variable with a possible null let user: User | null = null; @@ -157,4 +158,9 @@ export const actions: Actions = { return fail(500, { message: 'Something went wrong.' }); } } + + + + + }; diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index a55208c..2920999 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -112,8 +112,7 @@ - LeetCode (Coming Soon) + LeetCode Connect your LeetCode account to display your stats @@ -130,7 +129,6 @@ {:else} {/if} From 679382323830ddff4fd7e15d2193500cb27d8972 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Wed, 6 Nov 2024 02:22:28 +0400 Subject: [PATCH 04/13] Refactor LeetCode integration in MyProfile components --- .../components/MyProfile/LeetCodeForm.svelte | 8 ++-- .../components/MyProfile/LeetCodeStats.svelte | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 src/lib/components/MyProfile/LeetCodeStats.svelte diff --git a/src/lib/components/MyProfile/LeetCodeForm.svelte b/src/lib/components/MyProfile/LeetCodeForm.svelte index 4a9dabf..7750a74 100644 --- a/src/lib/components/MyProfile/LeetCodeForm.svelte +++ b/src/lib/components/MyProfile/LeetCodeForm.svelte @@ -1,14 +1,14 @@ + +
+

LeetCode Stats for {leetCodeUsername}

+ {#if data} +
    +
  • Active Years: {data.userCalendar.activeYears.join(', ')}
  • +
  • Current Streak: {data.userCalendar.streak} days
  • +
  • Total Active Days: {data.userCalendar.totalActiveDays}
  • +
  • Daily Coding Challenge Badges: +
      + {#each data.userCalendar.dccBadges as badge} +
    • + {badge.badge.name} + {badge.badge.name} - Earned on {new Date(badge.timestamp).toLocaleDateString()} +
    • + {/each} +
    +
  • + +
+ {:else} +

Loading LeetCode stats...

+ {/if} +
\ No newline at end of file From f3a50495340b6370097cb69da4deb816f5f85206 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Wed, 6 Nov 2024 02:22:56 +0400 Subject: [PATCH 05/13] Refactor LeetCode integration schema and types --- src/lib/schemas/integration-leetcode.ts | 2 +- src/lib/types/LeetCodeData.ts | 21 ++++++++ src/lib/types/PublicProfile.ts | 4 +- src/lib/utils/createRecentActivity.ts | 4 +- src/lib/utils/leetcode/leetcode.ts | 38 ++++++++++++++ src/routes/[username]/+page.server.ts | 9 +++- .../profile/integrations/+page.server.ts | 50 ++++++++++++++++-- src/routes/profile/integrations/+page.svelte | 52 ++++++++++--------- 8 files changed, 149 insertions(+), 31 deletions(-) create mode 100644 src/lib/types/LeetCodeData.ts create mode 100644 src/lib/utils/leetcode/leetcode.ts diff --git a/src/lib/schemas/integration-leetcode.ts b/src/lib/schemas/integration-leetcode.ts index 602ccb3..0b6ea28 100644 --- a/src/lib/schemas/integration-leetcode.ts +++ b/src/lib/schemas/integration-leetcode.ts @@ -5,7 +5,7 @@ export const leetCodeSchema = z.object({ }); -export type LeetcodeSchema = typeof leetCodeSchema; +export type LeetCodeSchema = typeof leetCodeSchema; diff --git a/src/lib/types/LeetCodeData.ts b/src/lib/types/LeetCodeData.ts new file mode 100644 index 0000000..a8c2418 --- /dev/null +++ b/src/lib/types/LeetCodeData.ts @@ -0,0 +1,21 @@ +export type LeetCodeStats = { + username: string; + userCalendar: { + activeYears: number[]; + streak: number; + totalActiveDays: number; + dccBadges: { + timestamp: string; + badge: { + name: string; + icon: string; + }; + }[]; + submissionCalendar: string; + }; + }; + + export type LanguageStats = { + languageName: string; + problemsSolved: number; + }; \ No newline at end of file diff --git a/src/lib/types/PublicProfile.ts b/src/lib/types/PublicProfile.ts index cfe14c5..56b5a4f 100644 --- a/src/lib/types/PublicProfile.ts +++ b/src/lib/types/PublicProfile.ts @@ -1,4 +1,4 @@ -import type { CryptoWallets, PersonalInformation, Social } from '@prisma/client'; +import type { CryptoWallets, PersonalInformation, Social, IntegrationLeetCode} from '@prisma/client'; export interface PublicProfile { links: Array<{ title: string; url: string }>; @@ -10,4 +10,6 @@ export interface PublicProfile { personalInformation: PersonalInformation | null; chessComUsername: string | null; crypto: CryptoWallets[]; + // TODO add leetCode to the userData + leetCode: IntegrationLeetCode | null; } diff --git a/src/lib/utils/createRecentActivity.ts b/src/lib/utils/createRecentActivity.ts index 83e8a5c..5b7d320 100644 --- a/src/lib/utils/createRecentActivity.ts +++ b/src/lib/utils/createRecentActivity.ts @@ -17,7 +17,9 @@ export const createRecentActivity = async ( | 'CHESS_COM_DELETED' | 'PERSONAL_INFORMATION_UPDATED' | 'CRYPTO_CREATED' - | 'CRYPTO_DELETED', + | 'CRYPTO_DELETED' + | 'LEETCODE_LINKED' + | 'LEETCODE_UNLINKED', activityDescription: string, userId: number ): Promise => { diff --git a/src/lib/utils/leetcode/leetcode.ts b/src/lib/utils/leetcode/leetcode.ts new file mode 100644 index 0000000..7ca96c4 --- /dev/null +++ b/src/lib/utils/leetcode/leetcode.ts @@ -0,0 +1,38 @@ +import type { LeetCodeStats } from '$lib/types/LeetCodeData'; + +export async function fetchLeetCodeStats(username: string): Promise { + const query = ` + query userProfileCalendar($username: String!, $year: Int) { + matchedUser(username: $username) { + username + userCalendar(year: $year) { + activeYears + streak + totalActiveDays + dccBadges { + timestamp + badge { + name + icon + } + } + submissionCalendar + } + } + } + `; + + const response = await fetch('https://leetcode.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables: { username }, + }), + }); + + const data = await response.json(); + return data?.data?.matchedUser || null; +} \ No newline at end of file diff --git a/src/routes/[username]/+page.server.ts b/src/routes/[username]/+page.server.ts index bd52064..e1da7ed 100644 --- a/src/routes/[username]/+page.server.ts +++ b/src/routes/[username]/+page.server.ts @@ -60,6 +60,10 @@ export const load: PageServerLoad = async ({ params }) => { const crypto = await prisma.cryptoWallets.findMany({ where: { userId: user.githubId } }); + const leetCode = await prisma.integrationLeetCode.findUnique({ + where: { userId: user.githubId } + }); + const userData: PublicProfile = { links, @@ -70,7 +74,10 @@ export const load: PageServerLoad = async ({ params }) => { username: username, isOpenToCollaborating: isOpenToCollaborating?.openToCollaborating, hobbies, - crypto + crypto, + // TODO add leetCode to the userData + leetCode + }; return { diff --git a/src/routes/profile/integrations/+page.server.ts b/src/routes/profile/integrations/+page.server.ts index ba6e388..7d61690 100644 --- a/src/routes/profile/integrations/+page.server.ts +++ b/src/routes/profile/integrations/+page.server.ts @@ -9,7 +9,7 @@ import { getGitHubUserIdFromImageUrl } from '$lib/utils/getGithubIDFromImage'; import { createRecentActivity } from '$lib/utils/createRecentActivity'; import { unlinkSpotify } from '$lib/utils/spotify/unlinkSpotify'; import { chessComSchema } from '$lib/schemas/integration-chesscom'; -import { leetCodeSchema } from '$lib/schemas/integration-leetcode'; +import { leetCodeSchema } from '$lib/schemas/integration-leetcode'; // Define the user variable with a possible null let user: User | null = null; @@ -24,7 +24,7 @@ export const load: PageServerLoad = async (event) => { // Fetch the user from the database user = await prisma.user.findUnique({ where: { githubId: userId } - }); + }); }; export const actions: Actions = { @@ -157,7 +157,51 @@ export const actions: Actions = { console.log(err); return fail(500, { message: 'Something went wrong.' }); } - } + }, + + + createLeetCode: async (event) => { + const form = await superValidate(event, zod(leetCodeSchema)); + if (!form.valid) return fail(400, { form }); + + const { username } = form.data; + + if (user) { + try { + await prisma.integrationLeetCode.create({ + data: { + username, + userId: user.githubId + } + }); + + createRecentActivity( + 'LEETCODE_LINKED', + `Linked your LeetCode account (${username})`, + user.githubId + ); + } catch (error) { + console.error(error); + throw new Error('Failed to create LeetCode integration'); + } + } + return { form }; + }, + + deleteLeetCode: async ({ url }) => { + try { + if (user) { + await prisma.integrationLeetCode.delete({ + where: { userId: user.githubId } + }); + + createRecentActivity('LEETCODE_UNLINKED', `Unlinked your LeetCode account`, user.githubId); + } + } catch (error) { + console.error(error); + return fail(500, { message: 'Something went wrong.' }); + } + } diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index 2920999..55a2766 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -11,6 +11,8 @@ import ChessComForm from '$lib/components/MyProfile/ChessComForm.svelte'; import { IconChess, IconLink } from '@tabler/icons-svelte'; import ChessComStats from '$lib/components/MyProfile/ChessComStats.svelte'; + import LeetCodeForm from '$lib/components/MyProfile/LeetCodeForm.svelte'; + import LeetCodeStats from '$lib/components/MyProfile/LeetCodeStats.svelte'; export let data: PageData; @@ -109,31 +111,33 @@
- + + {#if data} - - LeetCode - - Connect your LeetCode account to display your stats - - - - {#if true} -
-
- -
-
- {:else} - - {/if} -
+ + +
+ + + {:else} + + + + + {/if} - - + From 5e9fea610fa3287c97b8831bb292ca037a463db6 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Wed, 6 Nov 2024 14:44:49 +0400 Subject: [PATCH 06/13] Refactor LeetCode integration to use server-side proxy --- .../components/MyProfile/LeetCodeStats.svelte | 1 + src/lib/utils/leetcode/leetcode.ts | 7 +++-- src/routes/api/leetcode.ts | 16 +++++++++++ src/routes/profile/+layout.server.ts | 10 ++++++- .../profile/integrations/+page.server.ts | 28 +++++++++---------- src/routes/profile/integrations/+page.svelte | 8 ++++-- 6 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 src/routes/api/leetcode.ts diff --git a/src/lib/components/MyProfile/LeetCodeStats.svelte b/src/lib/components/MyProfile/LeetCodeStats.svelte index 0fcc1a2..d6a4fe1 100644 --- a/src/lib/components/MyProfile/LeetCodeStats.svelte +++ b/src/lib/components/MyProfile/LeetCodeStats.svelte @@ -9,6 +9,7 @@ onMount(async () => { try { data = await fetchLeetCodeStats(leetCodeUsername); + console.log('LeetCode stats:', data); } catch (error) { console.error('Error fetching LeetCode stats:', error); } diff --git a/src/lib/utils/leetcode/leetcode.ts b/src/lib/utils/leetcode/leetcode.ts index 7ca96c4..f6d54a1 100644 --- a/src/lib/utils/leetcode/leetcode.ts +++ b/src/lib/utils/leetcode/leetcode.ts @@ -21,8 +21,8 @@ export async function fetchLeetCodeStats(username: string): Promise { + const response = await fetch('https://leetcode.com/graphql', { + headers: { + 'Content-Type': 'application/json' + } + }); + + const data = await response.json(); + return new Response(JSON.stringify(data), { + headers: { + 'Content-Type': 'application/json' + } + }); +}; \ No newline at end of file diff --git a/src/routes/profile/+layout.server.ts b/src/routes/profile/+layout.server.ts index a8a5f41..c1631d9 100644 --- a/src/routes/profile/+layout.server.ts +++ b/src/routes/profile/+layout.server.ts @@ -13,6 +13,7 @@ import { personalInformationSchema } from '$lib/schemas/personal-information'; import type { LayoutServerLoad } from '../$types'; import { chessComSchema } from '$lib/schemas/integration-chesscom'; import { cryptoSchema } from '$lib/schemas/crypto'; +import { leetCodeSchema } from '$lib/schemas/integration-leetcode'; // Define the user variable with a possible null let user: User | null = null; @@ -78,6 +79,10 @@ export const load: LayoutServerLoad = async (event) => { where: { userId: user.githubId } }); + const leetCodeUsername = await prisma.integrationLeetCode.findFirst({ + where: { userId: user.githubId } + }); + const crypto = await prisma.cryptoWallets.findMany({ where: { userId: user.githubId } }); @@ -97,6 +102,7 @@ export const load: LayoutServerLoad = async (event) => { const personalInformationForm = await superValidate(zod(personalInformationSchema)); const chessComForm = await superValidate(zod(chessComSchema)); const cryptoForm = await superValidate(zod(cryptoSchema)); + const leetCodeForm = await superValidate(zod(leetCodeSchema)); // Return data to the frontend return { @@ -109,6 +115,7 @@ export const load: LayoutServerLoad = async (event) => { socials, spotifyToken, chessComUsername, + leetCodeUsername, crypto, form: linksForm, skillsForm, @@ -116,6 +123,7 @@ export const load: LayoutServerLoad = async (event) => { socialsForm, personalInformationForm, chessComForm, - cryptoForm + cryptoForm, + leetCodeForm }; }; diff --git a/src/routes/profile/integrations/+page.server.ts b/src/routes/profile/integrations/+page.server.ts index 7d61690..a80d630 100644 --- a/src/routes/profile/integrations/+page.server.ts +++ b/src/routes/profile/integrations/+page.server.ts @@ -159,7 +159,6 @@ export const actions: Actions = { } }, - createLeetCode: async (event) => { const form = await superValidate(event, zod(leetCodeSchema)); if (!form.valid) return fail(400, { form }); @@ -188,21 +187,20 @@ export const actions: Actions = { return { form }; }, - deleteLeetCode: async ({ url }) => { - try { - if (user) { - await prisma.integrationLeetCode.delete({ - where: { userId: user.githubId } - }); - - createRecentActivity('LEETCODE_UNLINKED', `Unlinked your LeetCode account`, user.githubId); - } - } catch (error) { - console.error(error); - return fail(500, { message: 'Something went wrong.' }); + deleteLeetCode: async ({ url }) => { + try { + if (user) { + await prisma.integrationLeetCode.delete({ + where: { userId: user.githubId } + }); + + createRecentActivity('LEETCODE_UNLINKED', `Unlinked your LeetCode account`, user.githubId); } - } - + } catch (error) { + console.error(error); + return fail(500, { message: 'Something went wrong.' }); + } + } diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index 55a2766..f8d5e06 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -15,6 +15,7 @@ import LeetCodeStats from '$lib/components/MyProfile/LeetCodeStats.svelte'; export let data: PageData; + console.log(data);

- {#if data} + {#if data.leetCodeUsername} LeetCode @@ -125,7 +126,7 @@ Unlink LeetCode - +
@@ -135,9 +136,10 @@ title="LeetCode" > - + {/if} + From 76a5c87138a6bebbf9cad07c4506f92ee18ff6ae Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Sun, 10 Nov 2024 02:32:16 +0400 Subject: [PATCH 07/13] Enhance LeetCodeStats component to fetch and display detailed user statistics with difficulty breakdown --- .../components/MyProfile/LeetCodeStats.svelte | 111 ++++++++++++++---- 1 file changed, 91 insertions(+), 20 deletions(-) diff --git a/src/lib/components/MyProfile/LeetCodeStats.svelte b/src/lib/components/MyProfile/LeetCodeStats.svelte index d6a4fe1..f93f68a 100644 --- a/src/lib/components/MyProfile/LeetCodeStats.svelte +++ b/src/lib/components/MyProfile/LeetCodeStats.svelte @@ -1,41 +1,112 @@ -
-

LeetCode Stats for {leetCodeUsername}

+ + +
+
+

LeetCode Stats for {leetCodeUsername}

+
+
+ {data?.userCalendar.streak} day streak +
+
+ {data?.userCalendar.totalActiveDays} active days +
+
+ Ranking: {data?.profile.ranking} +
+
+
+ {#if data} -
    -
  • Active Years: {data.userCalendar.activeYears.join(', ')}
  • -
  • Current Streak: {data.userCalendar.streak} days
  • -
  • Total Active Days: {data.userCalendar.totalActiveDays}
  • -
  • Daily Coding Challenge Badges: -
      + {#if data.userCalendar.dccBadges.length > 0} +
      +

      Badges

      +
      {#each data.userCalendar.dccBadges as badge} -
    • - {badge.badge.name} - {badge.badge.name} - Earned on {new Date(badge.timestamp).toLocaleDateString()} -
    • +
      + {badge.badge.name} +
      {badge.badge.name}
      +
      {/each} -
    -
  • - -
+
+
+ {/if} + +
+

Problem Solving Summary

+
+
+
+
+
+
+
+
+
{easyCount} Easy
+
{mediumCount} Medium
+
{hardCount} Hard
+
+
+
+
{:else}

Loading LeetCode stats...

{/if} - \ No newline at end of file + From 098db17effa23182ed10678277be89e7cce80136 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Sun, 10 Nov 2024 02:32:52 +0400 Subject: [PATCH 08/13] Add LeetCodeStats component to PublicProfile for displaying user LeetCode statistics --- src/lib/components/PublicProfile/PublicProfile.svelte | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/components/PublicProfile/PublicProfile.svelte b/src/lib/components/PublicProfile/PublicProfile.svelte index 350e21e..a8ae962 100644 --- a/src/lib/components/PublicProfile/PublicProfile.svelte +++ b/src/lib/components/PublicProfile/PublicProfile.svelte @@ -8,10 +8,12 @@ import ProfileHero from '$lib/components/PublicProfile/ProfileHero.svelte'; import { Separator } from '$lib/components//ui/separator'; import ChessComStats from '$lib/components/MyProfile/ChessComStats.svelte'; + import LeetCodeStats from '$lib/components/MyProfile/LeetCodeStats.svelte'; // Accept userData as a prop export let userData: PublicProfile; - + console.log(userData); + console.log(userData.leetCode?.username) //Accept githubData as a prop export let githubData: GithubData | null; @@ -37,6 +39,11 @@ {#if userData.chessComUsername != null} {/if} + + + {#if userData.leetCode != null} + + {/if}
From 410eb4f4f2177b17fb742baf7f69dd9065ffa420 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Sun, 10 Nov 2024 02:33:27 +0400 Subject: [PATCH 09/13] Refactor LeetCodeData types to include user profile and global submission statistics --- src/lib/types/LeetCodeData.ts | 41 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/lib/types/LeetCodeData.ts b/src/lib/types/LeetCodeData.ts index a8c2418..55fb79f 100644 --- a/src/lib/types/LeetCodeData.ts +++ b/src/lib/types/LeetCodeData.ts @@ -1,21 +1,30 @@ export type LeetCodeStats = { username: string; userCalendar: { - activeYears: number[]; - streak: number; - totalActiveDays: number; - dccBadges: { - timestamp: string; - badge: { - name: string; - icon: string; - }; - }[]; - submissionCalendar: string; + activeYears: number[]; + streak: number; + totalActiveDays: number; + dccBadges: { + timestamp: string; + badge: { + name: string; + icon: string; + }; + }[]; }; - }; - - export type LanguageStats = { - languageName: string; + profile: { + ranking: number; + }; + submitStatsGlobal:{ + acSubmissionNum: { + difficulty: string; + count: number; + }[]; + } +}; + +export type TagProblem = { + tagName: string; + tagSlug: string; problemsSolved: number; - }; \ No newline at end of file +}; From 8b0ddd7bd7ccd5fa1d93d401d1c34c09e707d4b1 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Sun, 10 Nov 2024 02:33:40 +0400 Subject: [PATCH 10/13] Add leetCode integration to PublicProfile type --- src/lib/types/PublicProfile.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/types/PublicProfile.ts b/src/lib/types/PublicProfile.ts index 56b5a4f..6e79b0f 100644 --- a/src/lib/types/PublicProfile.ts +++ b/src/lib/types/PublicProfile.ts @@ -10,6 +10,5 @@ export interface PublicProfile { personalInformation: PersonalInformation | null; chessComUsername: string | null; crypto: CryptoWallets[]; - // TODO add leetCode to the userData leetCode: IntegrationLeetCode | null; } From 969bbe83d574aa826727ad2a5858bb76944f4d80 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Sun, 10 Nov 2024 02:33:50 +0400 Subject: [PATCH 11/13] Refactor LeetCode integration: remove old API handler and implement new server-side proxy for fetching user statistics --- src/lib/utils/leetcode/leetcode.ts | 39 -------------- src/routes/api/leetcode.ts | 16 ------ src/routes/api/leetcode/+server.ts | 56 ++++++++++++++++++++ src/routes/profile/integrations/+page.svelte | 4 -- 4 files changed, 56 insertions(+), 59 deletions(-) delete mode 100644 src/lib/utils/leetcode/leetcode.ts delete mode 100644 src/routes/api/leetcode.ts create mode 100644 src/routes/api/leetcode/+server.ts diff --git a/src/lib/utils/leetcode/leetcode.ts b/src/lib/utils/leetcode/leetcode.ts deleted file mode 100644 index f6d54a1..0000000 --- a/src/lib/utils/leetcode/leetcode.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { LeetCodeStats } from '$lib/types/LeetCodeData'; - -export async function fetchLeetCodeStats(username: string): Promise { - const query = ` - query userProfileCalendar($username: String!, $year: Int) { - matchedUser(username: $username) { - username - userCalendar(year: $year) { - activeYears - streak - totalActiveDays - dccBadges { - timestamp - badge { - name - icon - } - } - submissionCalendar - } - } - } - `; - // call is made through a server-side proxy - const response = await fetch('/api/leetcode', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query, - variables: { username }, - }), - }); - - const data = await response.json(); - return data?.data?.matchedUser || null; -} - diff --git a/src/routes/api/leetcode.ts b/src/routes/api/leetcode.ts deleted file mode 100644 index c18d7d8..0000000 --- a/src/routes/api/leetcode.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { RequestHandler } from '@sveltejs/kit'; - -export const GET: RequestHandler = async ({ url }) => { - const response = await fetch('https://leetcode.com/graphql', { - headers: { - 'Content-Type': 'application/json' - } - }); - - const data = await response.json(); - return new Response(JSON.stringify(data), { - headers: { - 'Content-Type': 'application/json' - } - }); -}; \ No newline at end of file diff --git a/src/routes/api/leetcode/+server.ts b/src/routes/api/leetcode/+server.ts new file mode 100644 index 0000000..83da272 --- /dev/null +++ b/src/routes/api/leetcode/+server.ts @@ -0,0 +1,56 @@ +import type { RequestHandler } from '@sveltejs/kit'; + +export const GET: RequestHandler = async ({ url }) => { + const username = url.searchParams.get('leetCodeUsername'); + const query = ` + query userProfileCalendar($username: String!) { + matchedUser(username: $username) { + username + userCalendar { + activeYears + streak + totalActiveDays + dccBadges { + timestamp + badge { + name + icon + } + } + } + + profile { + ranking + } + + submitStatsGlobal { + acSubmissionNum { + difficulty + count + } + } + } + }`; + const variables = { + username, + }; + + const response = await fetch('https://leetcode.com/graphql', { + headers: { + 'Content-Type': 'application/json' + }, + method: 'POST', + body: JSON.stringify({ + operationName: 'userProfileCalendar', + query, + variables + }) + }); + + const data = await response.json(); + return new Response(JSON.stringify(data), { + headers: { + 'Content-Type': 'application/json' + } + }); +}; \ No newline at end of file diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index f8d5e06..e214a94 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -116,9 +116,6 @@ {#if data.leetCodeUsername} LeetCode - - You can unlink your LeetCode account here to showcase your stats. -
@@ -135,7 +132,6 @@ description="Link your LeetCode account to showcase your stats." title="LeetCode" > - {/if} From 383cdc1bfce57a343b92a4211c1ff65588e936d9 Mon Sep 17 00:00:00 2001 From: omaramkotb22 Date: Sun, 10 Nov 2024 13:42:31 +0400 Subject: [PATCH 12/13] Enhance LeetCodeStats component: add loading state, improve layout with Card and Table components, and display user statistics with badges and problem-solving summary --- .../components/MyProfile/LeetCodeStats.svelte | 223 ++++++++++-------- .../PublicProfile/PublicProfile.svelte | 2 - src/routes/profile/+page.svelte | 10 + src/routes/profile/integrations/+page.svelte | 10 +- 4 files changed, 135 insertions(+), 110 deletions(-) diff --git a/src/lib/components/MyProfile/LeetCodeStats.svelte b/src/lib/components/MyProfile/LeetCodeStats.svelte index f93f68a..8836019 100644 --- a/src/lib/components/MyProfile/LeetCodeStats.svelte +++ b/src/lib/components/MyProfile/LeetCodeStats.svelte @@ -1,112 +1,135 @@ -
-
-

LeetCode Stats for {leetCodeUsername}

-
-
- {data?.userCalendar.streak} day streak -
-
- {data?.userCalendar.totalActiveDays} active days -
-
- Ranking: {data?.profile.ranking} -
-
-
+ + + LeetCode + {leetCodeUsername}'s LeetCode stats + + + + {#if loading} +

Loading...

+ {:else if data} + + + + + Metric + Value + + + + + Streak + {data.userCalendar.streak} days + + + Active Days + {data.userCalendar.totalActiveDays} + + + Ranking + {data.profile.ranking} + + + - {#if data} - {#if data.userCalendar.dccBadges.length > 0} -
-

Badges

-
- {#each data.userCalendar.dccBadges as badge} -
- {badge.badge.name} -
{badge.badge.name}
-
- {/each} -
-
- {/if} + + {#if data.userCalendar.dccBadges.length > 0} +

Badges

+
+ {#each data.userCalendar.dccBadges as badge} +
+ {badge.badge.name} +
{badge.badge.name}
+
+ {/each} +
+ {/if} -
-

Problem Solving Summary

-
-
-
-
-
-
-
-
-
{easyCount} Easy
-
{mediumCount} Medium
-
{hardCount} Hard
-
-
-
-
- {:else} -

Loading LeetCode stats...

- {/if} -
+ +

Problem Solving Summary

+ + + + Difficulty + Count + + + + + Easy + {easyCount} + + + Medium + {mediumCount} + + + Hard + {hardCount} + + + + {:else} +

No data available.

+ {/if} + + diff --git a/src/lib/components/PublicProfile/PublicProfile.svelte b/src/lib/components/PublicProfile/PublicProfile.svelte index a8ae962..9e53191 100644 --- a/src/lib/components/PublicProfile/PublicProfile.svelte +++ b/src/lib/components/PublicProfile/PublicProfile.svelte @@ -12,8 +12,6 @@ // Accept userData as a prop export let userData: PublicProfile; - console.log(userData); - console.log(userData.leetCode?.username) //Accept githubData as a prop export let githubData: GithubData | null; diff --git a/src/routes/profile/+page.svelte b/src/routes/profile/+page.svelte index 4c5524a..3f57f51 100644 --- a/src/routes/profile/+page.svelte +++ b/src/routes/profile/+page.svelte @@ -75,6 +75,16 @@ {/if} + + LeetCode + + {#if data.leetCodeUsername} + + {:else} + Not Linked + {/if} + + diff --git a/src/routes/profile/integrations/+page.svelte b/src/routes/profile/integrations/+page.svelte index e214a94..a796486 100644 --- a/src/routes/profile/integrations/+page.svelte +++ b/src/routes/profile/integrations/+page.svelte @@ -10,12 +10,12 @@ import MusicPlayer from '$lib/components/Shared/MusicPlayer.svelte'; import ChessComForm from '$lib/components/MyProfile/ChessComForm.svelte'; import { IconChess, IconLink } from '@tabler/icons-svelte'; + import { Braces } from 'lucide-svelte'; import ChessComStats from '$lib/components/MyProfile/ChessComStats.svelte'; import LeetCodeForm from '$lib/components/MyProfile/LeetCodeForm.svelte'; import LeetCodeStats from '$lib/components/MyProfile/LeetCodeStats.svelte'; export let data: PageData; - console.log(data);

-
{#if data.chessComUsername} @@ -114,19 +113,14 @@
{#if data.leetCodeUsername} - - LeetCode -
+ -
-
-
{:else} Date: Wed, 13 Nov 2024 13:42:51 +0400 Subject: [PATCH 13/13] Changed Ordering of the Stats, Applied Tailwind CSS --- .../components/MyProfile/LeetCodeStats.svelte | 95 ++++++++----------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/src/lib/components/MyProfile/LeetCodeStats.svelte b/src/lib/components/MyProfile/LeetCodeStats.svelte index 8836019..e17fb16 100644 --- a/src/lib/components/MyProfile/LeetCodeStats.svelte +++ b/src/lib/components/MyProfile/LeetCodeStats.svelte @@ -35,28 +35,6 @@ }); - - LeetCode @@ -67,64 +45,64 @@ {#if loading}

Loading...

{:else if data} - - + +

Problem Solving Summary

+ - Metric - Value + Difficulty + Count - Streak - {data.userCalendar.streak} days + Easy + {easyCount} - Active Days - {data.userCalendar.totalActiveDays} + Medium + {mediumCount} - Ranking - {data.profile.ranking} + Hard + {hardCount} {#if data.userCalendar.dccBadges.length > 0} -

Badges

-
+

Badges

+
{#each data.userCalendar.dccBadges as badge} -
- {badge.badge.name} -
{badge.badge.name}
+
+ {badge.badge.name} +
{/each}
{/if} - -

Problem Solving Summary

- - - - Difficulty - Count - - + +

Metrics

+ - - Easy - {easyCount} + + + Longest Streak: + {data.userCalendar.streak} days - - Medium - {mediumCount} + + + Active Days: + {data.userCalendar.totalActiveDays} - - Hard - {hardCount} + + + Ranking: + {data.profile.ranking} @@ -133,3 +111,10 @@ {/if} + +